package cc.magicjson.factory;

import org.springframework.beans.factory.config.YamlPropertiesFactoryBean;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.support.DefaultPropertySourceFactory;
import org.springframework.core.io.support.EncodedResource;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;

import java.io.IOException;
import java.util.Objects;

import static java.util.Objects.nonNull;
import static org.apache.commons.lang3.StringUtils.endsWithAny;

/**
 * 扩展 {@link DefaultPropertySourceFactory} 以支持 YAML 文件.
 * 这个工厂类可以创建支持 YAML 和 properties 文件的 PropertySource
 *
 * @author <a href="mailto:it_magicjson@163.com">MagicJson</a>
 * @since 1.0.0
 */
public class YamlPropertySourceFactory extends DefaultPropertySourceFactory {

    /**
     * YAML 文件的可能扩展名数组。
     */
    private static final String[] YAML_EXTENSIONS = {".yaml", ".yml"};

    /**
     * 创建支持 YAML 文件的 PropertySource。
     * 如果文件是 YAML 格式，则创建 YAML 特定的 PropertySource；
     * 否则，委托给父类处理。
     *
     * @param name     资源名称，可为空
     * @param resource 编码资源
     * @return PropertySource 实例
     * @throws IOException 如果资源读取失败
     */
    @Override
    public @NonNull PropertySource<?> createPropertySource(@Nullable String name, @NonNull EncodedResource resource) throws IOException {
        String sourceName = determineSourceName(name, resource);
        if (isYamlFile(sourceName)) {
            return createYamlPropertySource(sourceName, resource);
        }
        return super.createPropertySource(name, resource);
    }

    /**
     * 确定属性源的名称。
     * 如果提供了名称，则使用该名称；否则，尝试从资源中获取文件名。
     *
     * @param name 可能为null的用户提供的名称
     * @param resource 非null的编码资源对象
     * @return 确定的属性源名称
     * @throws NullPointerException 如果name为null且无法从资源中获取文件名
     */
    private String determineSourceName(@Nullable String name, @NonNull EncodedResource resource) {
        if (nonNull(name)) {
            return name;
        }
        String filename = resource.getResource().getFilename();
        return Objects.requireNonNull(filename, "Resource filename must not be null");
    }

    /**
     * 检查给定的文件名是否为 YAML 文件。
     *
     * @param filename 要检查的文件名
     * @return 如果文件名以 .yaml 或 .yml 结尾（不区分大小写），则返回 true
     */
    private boolean isYamlFile(String filename) {
        return filename != null && endsWithAny(filename.toLowerCase(), YAML_EXTENSIONS);
    }

    /**
     * 创建 YAML 特定的 PropertySource。
     *
     * @param sourceName 属性源的名称
     * @param resource 包含 YAML 内容的编码资源
     * @return 基于 YAML 内容的 PropertySource
     */
    private PropertySource<?> createYamlPropertySource(String sourceName, EncodedResource resource) {
        YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean();
        factoryBean.setResources(resource.getResource());
        factoryBean.afterPropertiesSet();
        return new PropertiesPropertySource(sourceName, Objects.requireNonNull(factoryBean.getObject(), "YAML properties must not be null"));
    }
}
